/***************************************************************************
 *   Copyright (C) 2007 by Anders Nore																		 *
 *   andenore@start.no  																									 *
 *                                                                         *
 *   Permission is hereby granted, free of charge, to any person obtaining *
 *   a copy of this software and associated documentation files (the       *
 *   "Software"), to deal in the Software without restriction, including   *
 *   without limitation the rights to use, copy, modify, merge, publish,   *
 *   distribute, sublicense, and/or sell copies of the Software, and to    *
 *   permit persons to whom the Software is furnished to do so, subject to *
 *   the following conditions:                                             *
 *                                                                         *
 *   The above copyright notice and this permission notice shall be        *
 *   included in all copies or substantial portions of the Software.       *
 *                                                                         *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       *
 *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    *
 *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. *
 *   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR     *
 *   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, *
 *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR *
 *   OTHER DEALINGS IN THE SOFTWARE.                                       *
 ***************************************************************************/

#define THREADED

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdio.h>
#include <stdlib.h>

#include "unp.h"
#include "duxinterface.h"
#include "thread.h"
#include "mcastchat.h"
#include "extra.h"

void *send_text(void *arg);
void usage(char *arg1);
void *doexit(void);

/**
* Variables
*/

int sockfd;															/* GLOBAL socket descriptor */
char *str_interface_name, *str_multicast_addr;	/* options */
int enable_colors = 0;
struct sockaddr_in grpaddr, listenaddr;	/* multicast group address and local listenaddress */
WINDOW *mainwin, *chatbar;							/* GLOBAL ncurses windows, user interface */
int maxx, maxy;													/* GLOBAL maxx and maxy window column/lines */
pthread_t send_thread;									/* our sending thread */

mutex_t mtx_scr;	/* not so fine grained mutex for protection of our stdscr pointer without this 
									   strange things WILL occur as text is printed at random places across the screeen */

/* GLOBAL Multicast group variables */
struct sockaddr joined_groups[MAX_GROUP_JOINS]; /* holds all of our joined 
																									 multicastgroups 				*/
int num_joined_groups = 0;	/* holds our index into joined_groups */

/* GLOBAL History variables */
char history[MAX_HISTORY][MAX_LINE];
int history_index = 0;
mutex_t history_mutex;

int main(int argc, char *argv[])
{
	int c;
	socklen_t len;
		
	str_interface_name = str_multicast_addr = NULL;
	pthread_mutex_init(&mtx_scr, NULL);
	
	atexit((void *)doexit);
	
	/**
	* Parse commandline options
	*/
	while( (c = getopt(argc, argv, "hci:m:")) != -1) {
		switch(c) {
			case 'i':
				str_interface_name = optarg;
				break;
			case 'm':
				str_multicast_addr = optarg;
				break;
			case 'c':
				enable_colors = 1;
				break;
			case '?':
				if(isprint( optopt )) {
					err_quit("Option [-%s] requires an argument or does not exist.\n", optopt);
				} else {
					err_quit("Unknown option character `\\x%x'.\n", optopt);
				}
				usage(argv[0]);
				break;
			case 'h':
			default:
				usage(argv[0]);
				break;
		}
	}
	
	/* Select default multicast address if none specified */
	if(str_multicast_addr == NULL) {
		static char *multiaddr = DEFAULT_MULTICAST_ADDR;
		str_multicast_addr = multiaddr;
	}
	
	mainwin = initscr();		/* initialize ncurses tui(terminal user interface) */
	initializeColors();
	getmaxyx(stdscr, maxy, maxx);
	setupMainWindow(mainwin);
	chatbar = setupChatBar();
	refreshChatBar(chatbar);
	
	setColor(mainwin, COLOR_DEFAULT);
	setColor(chatbar, COLOR_DEFAULT);
	
	bzero(&listenaddr, sizeof(listenaddr));
	listenaddr.sin_family = AF_INET;
	listenaddr.sin_port = htons(SERV_PORT);
	if(str_interface_name == NULL)
		listenaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	else {
		/** XXX: Funka ikkje som planlagt */
		inet_pton(AF_INET, str_interface_name, &listenaddr.sin_addr);
	}
	
	sockfd = Socket( AF_INET, SOCK_DGRAM, NULL );
	Bind(sockfd, (SA*)&listenaddr, sizeof(listenaddr));
	
  bzero(&grpaddr, sizeof(grpaddr));
  grpaddr.sin_family = AF_INET;
	grpaddr.sin_port = htons(SERV_PORT);
	grpaddr.sin_addr.s_addr = inet_addr(str_multicast_addr);		/* join this multicast group */
	
	/* Zero out our joined groups array */
	bzero(joined_groups, sizeof(joined_groups));
	/* Initializes our history */
	initializeHistory();
	
	/* Join specified or default multicastgroup */
	Mcast_join(sockfd, (SA *)&grpaddr, sizeof(grpaddr), NULL, str_interface_name);
	
	Mcast_enable_loop(sockfd, 1);	/* make sure we get our own packages */
	if( pthread_create(&send_thread, NULL, &send_text, NULL) != 0) 
		err_quit("pthread_create()::error");
	
	int n = 1;
	struct packetheader receiveHeader;
	while( 1 ) {
		bzero(&receiveHeader, sizeof(receiveHeader));
		int size = sizeof(listenaddr);
		n = recvfrom( sockfd, &receiveHeader, sizeof(receiveHeader), 0, (SA*)&listenaddr, &size);
		
		pthread_mutex_lock(&mtx_scr);
		
		if(receiveHeader.magicnum != MAGIC_NUM) {
			DEBUG_PRINT_HEX("Received invalid packet with magicnum:", receiveHeader.magicnum);
			pthread_mutex_unlock(&mtx_scr);	/* make sure we unlock before we continue */
			continue;	/* start new loop if it's not a valid packet */
		}	
		
		if( n == 0 ) {	
			/* received eof */
			exit(0);
		} else if( n == -1) {		
			/* unexpected error */
			err_quit("Reading from socked descriptor failed\n");
		} else {	
			/* read n bytes from socket */
			receiveHeader.message[sizeof(receiveHeader.message) - 1] = '\0';
			waddstr(mainwin, Sock_ntop((SA *)&listenaddr, size));
			waddstr(mainwin, " : ");
			waddstr(mainwin, receiveHeader.message);
			waddstr(mainwin, "\n"); /* line break */
		}
		
		wrefresh(mainwin);
		pthread_mutex_unlock(&mtx_scr);
	} 
	
  return EXIT_SUCCESS;
}

void *send_text(void *arg) {
	char sendbuf[MAX_LINE];
	
	static int c = 0;	/* current character sendt to chatbar */
	int charIndex = 0; /* index into sendbuf */
	int histind = 0; /* index into history */
	
	/* Setup our packetheader */
	struct packetheader pkt_hdr;
	bzero(&pkt_hdr, sizeof(pkt_hdr));
	pkt_hdr.magicnum = MAGIC_NUM;
	
	while((c = wgetch(chatbar)) != KEY_F(1)) { 
		pthread_mutex_lock(&mtx_scr);
		if( c == KEY_ENTER || c == '\r' || c == '\n') {
			if(charIndex > 0) {	
				/* Only send data if we have any */ 
				
				/* Send data if it is not a command */
				if( checkInput(sendbuf) == CMD_NOCOMMAND) {
					/* overwrite packetheader message with sendbuf each iteration */
					memcpy(&pkt_hdr.message, (char *)&sendbuf, sizeof(pkt_hdr.message));
					sendto(sockfd, &pkt_hdr, sizeof(pkt_hdr), NULL, (SA *)&grpaddr, sizeof(grpaddr));
				}
				
				/* Save to history and clear chatbar and sendbuf */
				addHistory(sendbuf);
				histind = 0;
				charIndex = 0;
				bzero(&pkt_hdr.message, sizeof(pkt_hdr.message));
				bzero(&sendbuf, sizeof(sendbuf));
				wclear(chatbar);
				refreshChatBar(chatbar);
				
			}
		} else if( c == KEY_BACKSPACE || c == '\b' || c == 0x7f) {
			// 0x7f = ^?, backspace skal vere ^H. Enten terminal eller curses sin feil.
			if(charIndex > 0) {
				mvwdelch(chatbar, 2, charIndex);
				charIndex--;
				sendbuf[charIndex] = '\0';	/* null terminate our sending buffer as we delete characters */
			}
		} else if( c == KEY_UP || c == KEY_DOWN ) {
			/* Get history */
			char *string;
			
			wclear(chatbar);
			refreshChatBar(chatbar);
			
			if(histind < 0)
				histind = 0;
			else if(histind > MAX_HISTORY)
				histind = MAX_HISTORY;
			
			if(c == KEY_DOWN) {
				histind--;
				string = getHistory(histind - 1);
			} else {
				string = getHistory(histind);
				histind++;
			}
				
			if(string != NULL) {
				waddstr(chatbar, string);
	// 			DEBUG_PRINT_INT("strlen(string):", strlen(string));
				strncpy(&sendbuf[0], string, strlen(string));
				sendbuf[strlen(string)] = '\0';
				charIndex = strlen(string);
				
			} else {
				charIndex = 0;
				bzero(&sendbuf, sizeof(sendbuf));
			}
		
		} else if( c == KEY_LEFT)  {
			wmove(chatbar, 2, --charIndex);
		} else if( c == KEY_RIGHT) {
			wmove(chatbar, 2, ++charIndex);
		} else {
			/* Add characters to the buffer */
			if(charIndex < MAX_LINE && isprint(c)) {
				histind = 0;
				sendbuf[charIndex] = c;
				charIndex++;
				waddch(chatbar, c);
			}
		}
		wrefresh(chatbar);
		pthread_mutex_unlock(&mtx_scr);
	}
	
	exit(0);
}

/* Print usage message and exit */
void usage(char *arg1) {
	printf("Usage:  %s [-h] [-c] [-i] <interface> [-m] <multicast group>\n", arg1);
	exit(0);
}

/** 
* Make sure we end curses or else ugly things might happen to the console
*/
void *doexit(void) {
	delwin(chatbar);
	delwin(mainwin);
	endwin();
}

